let data = [
  {
    id: 1,
    label: '一级 1',
    children: [
      {
        id: 4,
        label: '二级 1-1',
        children: [
          {
            id: 9,
            label: '三级 1-1-1',
          },
          {
            id: 10,
            label: '三级 1-1-2',
          },
        ],
      },
    ],
  },
  {
    id: 2,
    label: '一级 2',
    children: [
      {
        id: 5,
        label: '二级 2-1',
      },
      {
        id: 6,
        label: '二级 2-2',
      },
    ],
  },
  {
    id: 3,
    label: '一级 3',
    children: [
      {
        id: 7,
        label: '二级 3-1',
      },
      {
        id: 8,
        label: '二级 3-2',
      },
    ],
  },
];

{
  // 新增节点
  const appendNodeInTree = (id, tree, obj) => {
    tree.forEach((ele) => {
      if (ele.id === id) {
        ele.children ? ele.children.push(obj) : (ele.children = [obj]);
      } else {
        if (ele.children) {
          appendNodeInTree(id, ele.children, obj);
        }
      }
    });
    return tree;
  };
}

{
  // 删除节点
  const removeNodeInTree = (tree, id) => {
    // 通过id从数组（树结构）中移除元素
    if (!tree || !tree.length) {
      return;
    }
    for (let i = 0; i < tree.length; i++) {
      if (tree[i].id === id) {
        tree.splice(i, 1);
        break;
      }
      removeNodeInTree(tree[i].children, id);
    }
  };
}

{
  // 修改节点
  const updateNodeInTree = (tree, id, obj) => {
    if (!tree || !tree.length) {
      return;
    }
    for (let i = 0; i < tree.length; i++) {
      if (tree[i].id == id) {
        tree[i] = obj;
        break;
      }
      updateNodeInTree(tree[i].children, id, obj);
    }
  };
}

{
  // 查找节点
  const findNodeInTree = (data, key, callback) => {
    for (let i = 0; i < data.length; i++) {
      if (data[i].key == key) {
        return callback(data[i], i, data);
      }
      if (data[i].children) {
        findNodeInTree(data[i].children, key, callback);
      }
    }
  };

  // 所查找到的节点要存储的方法
  let obj = {};
  findNodeInTree(data, key, (item, index, arr) => {
    obj = item;
  });

  // 此时就是Obj对应的要查找的节点
  console.log(obj);
}

{
  // 查找 第一个无叶子节点
  const findNode = (tree, func) => {
    let node,
      list = [...tree];

    while ((node = list.shift())) {
      if (func(node)) {
        return node;
      }

      node.children && list.unshift(...node.children);
    }
  };

  const firstNoChildNode = findNode(data, (node) => {
    return !node.children;
  });

  console.log('第一个无叶子节点', firstNoChildNode);
}

{
  /**
   * 获取树节点路径
   * @param {*} curKey 树节点标识的值
   * @param {array} data 树
   * @returns {array} result 存放搜索到的树节点到顶部节点的路径节点
   */
  const getPathByKey = (curKey, data) => {
    // 存放搜索到的树节点到顶部节点的路径节点
    let result = [];

    /**
     * 路径节点追踪
     * @param {*} curKey 树节点标识的值
     * @param {array} path 存放搜索到的树节点到顶部节点的路径节点
     * @param {*} data 树
     * @returns undefined
     */
    let traverse = (curKey, path, data) => {
      // 树为空时，不执行函数
      if (data.length === 0) {
        return;
      }

      // 遍历存放树的数组
      for (let item of data) {
        // 遍历的数组元素存入path参数数组中
        path.push(item);
        // 如果目的节点的id值等于当前遍历元素的节点id值
        if (item.id === curKey) {
          // 把获取到的节点路径数组path赋值到result数组
          result = JSON.parse(JSON.stringify(path));
          return;
        }

        // 当前元素的children是数组
        const children = Array.isArray(item.children) ? item.children : [];
        // 递归遍历子数组内容
        traverse(curKey, path, children);
        // 利用回溯思想，当没有在当前叶树找到目的节点，依次删除存入到的path数组路径
        path.pop();
      }
    };
    traverse(curKey, [], data);
    // 返回找到的树节点路径
    return result;
  };

  // 对应树节点路径(数组形式)
  let pathList = getPathByKey(9, data);
  console.log('pathList', pathList);
  // 当前节点的父节点
  const parentNode = pathList[pathList.length - 2];
  console.log('parentNode: ', parentNode);
}
